/*=============================================================================
FILE: CGraphicFont.cpp

Font.
=============================================================================*/

#include "CGraphicFont.h"
#include "CTextParser.h"
#include "CMath.h"
#include "graphics.h"
#include "XCommon.h"
#include "CTextParser.h"

static CTextParser fontParser;
// TODO: deal with minCharSpacing and minLineSpacing more appropriately
// TODO: the following are not supported, maybe they will be in future:
// color 
// format


CGraphicFont::CGraphicFont()
{
	m_pImage = NULL;
	m_pHeaderData = NULL;
	m_pCharData = NULL;
	m_pControlCharData = NULL;
}


CGraphicFont::~CGraphicFont()
{
	SAFE_DELETE(m_pImage);
	SAFE_DELETE(m_pFontMetricsData);
}


void CGraphicFont::SetFontImage( const char * path )
{
	SAFE_DELETE(m_pImage);
	m_pImage = new CImage();
	m_pImage->Load(path);
} 


void CGraphicFont::SetFontMetrics( const char * path )
{
	ResFile file;
	Common::ReadFile(path,file);
	
	SAFE_DELETE(m_pFontMetricsData);
	m_pFontMetricsData = file.data;

	m_pHeaderData = (const tGTFHeader *) m_pFontMetricsData;

	// version check
	ASSERT( m_pHeaderData->m_versionGeneration == 1 );
	ASSERT( m_pHeaderData->m_versionMajor == 0 );
	ASSERT( m_pHeaderData->m_versionMinor == 0 );

	m_pCharData = NULL;
	if( m_pHeaderData->m_numChars > 0 )
	{
		m_pCharData = (const tGTFChar *) ( m_pFontMetricsData + sizeof( tGTFHeader ) );
	}	

	m_pControlCharData = NULL;	
	if( m_pHeaderData->m_numControlChars > 0 )
	{
		m_pControlCharData = (const tGTFControlChar *) ( m_pFontMetricsData + sizeof( tGTFHeader ) + ( m_pHeaderData->m_numChars * sizeof( tGTFChar ) ) );
	}
}


int CGraphicFont::GetFontHeight() const
{
	// SANITY CHECK
	ASSERT( m_pHeaderData != NULL );

	return (int) ( m_pHeaderData->m_height + m_pHeaderData->m_minLineSpacing );
}

int CGraphicFont::GetMinLineSpacing() const
{
    return m_pHeaderData->m_minLineSpacing;
}


int CGraphicFont::MeasureTextWidth( const AECHAR *pwszText, int numChars, int maxWidth, int *pCharsFit ) const
{
	boolean bDoAll = FALSE;
	int width = 0;
	int charsFit = 0;

	// SANITY CHECK
	ASSERT( pwszText != NULL );
	ASSERT( m_pHeaderData != NULL );

	// check character count
	if( numChars < 0 ) 
	{
		numChars = WSTRLEN( pwszText );	
	}

	// determine exit conditions
	if( maxWidth < 0 ) 
	{
		bDoAll = TRUE;
	}

	// cycle through each character until we are:
	//  a. out of characters
	//  b. out of pixels
	for( int i = 0; i < numChars; i++ )
	{
		// See if character fits in remaining width
        int charWidth = GetWidth( pwszText[i] );
		int deltaWidth = charWidth + (int)m_pHeaderData->m_minCharSpacing;
		if( !bDoAll && ( MAX( charWidth, deltaWidth ) > ( maxWidth - width ) ) ) 
		{
			break;
		}
		
		width += deltaWidth;
		charsFit++;
	}
    if( charsFit > 0 ) {
        width -= m_pHeaderData->m_minCharSpacing;
    }

	// output
	if( pCharsFit != NULL )
	{
		*pCharsFit = charsFit;
	}

	return width;
}


void CGraphicFont::PaintTextAlpha( const AECHAR *pwszText, int numChars, const _Rect &clip, int x, int y, float scale, int flags, uint32 alpha ) const
{
	// SANITY CHECK
	ASSERT( pwszText != NULL );
	ASSERT( m_pImage != NULL );
	ASSERT( m_pHeaderData != NULL );
	
	// calculate number of characters (if necessary)
	if( numChars < 0 )
	{
		numChars = WSTRLEN( pwszText );
	}

	// set up the colors
	if( TEST_MASK( flags, FONT_RENDER_LEFT ) )
	{
		x = clip.x;
	}
	else if( TEST_MASK( flags, FONT_RENDER_CENTER ) )
	{
		x = clip.x + ( clip.dx >> 1 ) - ( MeasureTextWidth( pwszText, numChars ) >> 1 );
	}
	else if( TEST_MASK( flags, FONT_RENDER_RIGHT ) )
	{
		x = clip.x + clip.dx - MeasureTextWidth( pwszText, numChars );
	}

	if( TEST_MASK( flags, FONT_RENDER_TOP ) )
	{
		y = clip.y;
	}
	else if( TEST_MASK( flags, FONT_RENDER_MIDDLE ) )
	{
		y = clip.y + ( clip.dy >> 1 ) - ( GetFontHeight() >> 1 );
	}
	else if( TEST_MASK( flags, FONT_RENDER_BOTTOM ) )
	{
		y = clip.y + clip.dy - GetFontHeight();
	}

	// paint
	for ( int i = 0; i < numChars; i++ )
	{
		// first, let's see if this is a control char
		const tGTFControlChar *pControlChar = GetControlChar( pwszText[i] );
		if( pControlChar != NULL )
		{
			// just advance the rendering position.
			x += (int) ( pControlChar->m_width + m_pHeaderData->m_minCharSpacing ) * scale;
		}
		else
		{
			// okay, it isn't, maybe it's a normal character?
			const tGTFChar *pChar = GetChar( pwszText[i] );
			if( pChar != NULL )
			{
				if (alpha == 255)
				{
					// draw
					m_pImage->BlitComplexTransform( 
						&clip, 
						(int) ( x + pChar->m_drawOffsetX ), (int) ( y + ( pChar->m_drawOffsetY * scale ) ),
						(int) pChar->m_drawWidth, (int) pChar->m_drawHeight,
						(int) pChar->m_imageOffsetX, (int) pChar->m_imageOffsetY,
						 TRANS_NONE, 0, scale
					);
				}
				else
				{
					m_pImage->BlitTransformEx(
					   &clip, 
					   (int16) ( x + pChar->m_drawOffsetX ), (int16) ( y + ( pChar->m_drawOffsetY * scale ) ),
					   (int16) pChar->m_drawWidth, (int16) pChar->m_drawHeight,
					   pChar->m_imageOffsetX, (int16) pChar->m_imageOffsetY,
					   TRANS_NONE, alpha
					   );
				}

				// advance the rendering position.
				x += (int) ( pChar->m_width + m_pHeaderData->m_minCharSpacing ) * scale;
			}
		}
	}
}

void CGraphicFont::PaintText( const AECHAR *pwszText, int x, int y, int w, int h, int lineH, int flags, boolean bIsCentreY) const
{ // h is use for FONT_RENDER_BOTTOM & FONT_RENDER_MIDDLE
	fontParser.Parse(this,pwszText,w);
	int line = fontParser.GetNumLines();
	if (bIsCentreY)
	{
		y -= (line-1) * lineH >> 1;
	}
	
	for (int i = 0; i < line; i++)
	{
		int idx = fontParser.GetIndexForLine(i);
		int numChars = fontParser.GetNumCharsForLine(i);
		_Rect rect(x, y, w, h);
		// Graphics::DrawRect(&rect, &rect, 1); // temp
		if (numChars > 0/* && y > 0 && y < CGameApp::ScreenHeight()*/)
		{
			PaintText(pwszText + idx, numChars, rect, x, y, 1, flags);
		}				
		y += lineH;
	}
}

void CGraphicFont::PaintText( const AECHAR *pwszText, int x, int y, int w, int h, int lineH, int maxLines, int startLine, int flags /*= FONT_RENDER_NONE*/, boolean bIsCentreY /*= FALSE*/) const
{
	fontParser.Parse(this,pwszText,w);
	int line = fontParser.GetNumLines(); //(maxLines > 0) ? CMath::Min(maxLines,parser.GetNumLines()) : parser.GetNumLines();
	if (bIsCentreY)
	{
		y -= (line-1) * lineH >> 1;
	}
	int start = CMath::Min(startLine, line);
	int inc = 0;
	while(inc < maxLines)
	{
		int idx = fontParser.GetIndexForLine(start + inc);
		int numChars = fontParser.GetNumCharsForLine(start + inc);
		_Rect rect(x, y, w, h);
		if (numChars > 0)
		{
			PaintText(pwszText + idx, numChars, rect, x, y, 1, flags);
		}		
		y += lineH;
		inc ++;
	}
	
}

void CGraphicFont::PaintText( const AECHAR *pwszText, int numChars, const _Rect &clip, int x, int y, float scale, int flags ) const
{
	// TODO : ZR add alpha effect
	PaintTextAlpha(pwszText, numChars, clip, x, y, scale, flags, 255);
}

void CGraphicFont::DrawString( const AECHAR *pwszText, int x, int y, int flags ) const
{
	_Rect clip(x, y, MeasureTextWidth(pwszText, -1), GetFontHeight());
	// set up the colors
	if( TEST_MASK( flags, FONT_RENDER_CENTER ) )
	{
		clip.x -= ( clip.dx >> 1 );
	}
	else if( TEST_MASK( flags, FONT_RENDER_RIGHT ) )
	{
		clip.x -= clip.dx;
	}
	
	if( TEST_MASK( flags, FONT_RENDER_MIDDLE ) )
	{
		clip.y -= ( clip.dy >> 1 );
	}
	else if( TEST_MASK( flags, FONT_RENDER_BOTTOM ) )
	{
		clip.y -= clip.dy;
	}
	
	PaintTextAlpha(pwszText, -1, clip, x, y, 1, FONT_RENDER_LEFT | FONT_RENDER_TOP, 255);
}

const tGTFChar *CGraphicFont::GetChar( AECHAR c ) const
{
	const tGTFChar *pOut = NULL;

	// SANITY CHECK
	ASSERT( m_pHeaderData != NULL );

	if( m_pHeaderData->m_numChars > 0 )
	{
		// SANITY CHECK
		ASSERT( m_pCharData != NULL );

		// do a binary search for the character
		int low = 0;
		int high = m_pHeaderData->m_numChars - 1;	
		
		while( low <= high )
		{
			int middle = (low + high) >> 1;

			const tGTFChar *pChar = &m_pCharData[middle];
			if( c == pChar->m_charCode ) //match
			{
				pOut = pChar;
				break;
			}
			else if( c < pChar->m_charCode )
			{
				high = middle - 1;		//search low end of array
			}
			else
			{
				low = middle + 1;		//search high end of array
			}
		}
	}

    return pOut;
}


const tGTFControlChar *CGraphicFont::GetControlChar( AECHAR c ) const
{
	const tGTFControlChar *pOut = NULL;

	// SANITY CHECK
	ASSERT( m_pHeaderData != NULL );

	if( m_pHeaderData->m_numControlChars > 0  )
	{
		// SANITY CHECK
		ASSERT( m_pControlCharData != NULL );

		// not many control characters, so a linear search is sufficient
		const tGTFControlChar *pControlChar = m_pControlCharData;
		for( int i = 0; i < m_pHeaderData->m_numControlChars; i++ )
		{
			if( pControlChar->m_charCode == c )
			{
				pOut = pControlChar;
				break;
			}

			// advance
			pControlChar++;
		}
	}

	return pOut;	
}


int CGraphicFont::GetWidth( AECHAR c ) const
{
	int width = 0;

	// SANITY CHECK
	ASSERT( m_pHeaderData != NULL );

	// first, let's see if this is a control char
	const tGTFControlChar *pControlChar = GetControlChar( c );
	if( pControlChar != NULL )
	{
		width = (int) pControlChar->m_width;
	}
	else
	{
		// okay, it isn't, maybe it's a normal character?
		const tGTFChar *pChar = GetChar( c );
		if( pChar != NULL )
		{
			width = (int) pChar->m_width;
		}
	}

	return width;
}
